import json
import time
import re
from typing import List, Dict, Any

from django.conf import settings

import traceback

import logging


logger = logging.getLogger('XApiTestServer')

"""
Convertor:
1. vue format convert to database format
2. database format convert to vue format
"""


def vue_var_to_db(var_type: str, var_value):
    """
    根据vue中变量中的type和value,转换成db中存的值
    :param var_type: str: 类型 (legal var_type: string float int file boolean )
    :param var_value: object: 待转换的值
    :return: ok or error ???
    """
    value = ''

    try:
        if var_type == 'string':
            value = str(var_value)
        elif var_type == 'float':
            value = float(var_value)
        elif var_type == 'int':
            value = int(var_value)
        elif var_type == "file":
            value = [var_value]
        elif var_type == 'boolean':
            if var_value == 'False':
                value = False
            elif var_value == 'True':
                value = True
            else:
                return 'exception'

    except ValueError:
        logger.error('{value}转换{type}失败'.format(type=var_type, value=var_value))
        return 'exception'

    return value


def vue_to_db_dict(body_type, body) -> dict:
    """
    用例的请求体，request中的data(或params)和headers存储的是dict
    :return: dict
    """
    if body_type == 'variables':
        var_item_dict = {}
        for item in body:
            var_tmp = {item["inputKey"]: vue_var_to_db(item["selectType"], item["inputValue"])}
            var_item_dict.update(var_tmp)
        return var_item_dict

    if body_type == 'headers':
        '''
            headers: [
                {
                  id: '1',
                  inputKey: 'headers23',
                  inputValue: '34'
                },
                {
                  id: '2',
                  inputKey: 'headers23',
                  inputValue: '34'
                }
              ],
              
            'headers': {'token': '$token'}},  
        '''
        headers_item_dict = {}
        for item in body:
            header_tmp = {item["inputKey"]: item["inputValue"]}
            headers_item_dict.update(header_tmp)
        return headers_item_dict


def vue_to_db_list(body_type, body) -> list:
    """
    用例和配置
    :return: list
    """
    if body_type == 'database':
        db_item_list = []
        for item in body:
            db_tmp = {item["key"]: vue_var_to_db(item["type"], item["value"])}
            db_item_list.append(db_tmp)
        return db_item_list

    if body_type == 'variables':
        var_item_list = []
        for item in body:
            var_tmp = {item["inputKey"]: vue_var_to_db(item["selectType"], item["inputValue"])}
            var_item_list.append(var_tmp)
        return var_item_list

    if body_type == 'extract':
        '''
            extract: [
              {
                id: '1',
                inputKey: 'extract12',
                inputValue: '34'
              }, ... ]
        '''
        ext_item_list = []
        for item in body:
            ext_tmp = {item["inputKey"]: item["inputValue"]}
            ext_item_list.append(ext_tmp)
        return ext_item_list

    if body_type == 'validate':
        '''
            'validate': [{
                id: '1',
                inputCheck: 'validate12',
                selectComparator: 'equals',
                selectType: 'string',
                inputExpected: '34'
            }, ...]
            
            'validate': [
		        {'equals': ['json.code', 0]}, ... ]
        '''
        var_item_list = []
        for item in body:
            var_tmp = {item["selectComparator"]:
                           [item["inputCheck"], vue_var_to_db(item["selectType"], item["inputExpected"])]}
            var_item_list.append(var_tmp)
        return var_item_list

    if body_type == 'parameters':
        '''
        'parameters':[
            {
                id: '1',
                inputKey: 'user',
                inputValue: '['test001','test002','test003']'
              }, ... ]
              
        'parameters': [
             {'user': "['test001','test002','test003']"}, 
             {'user-passwd': "[['test001','111111'],['test002', '222222'],['test003','333333']]"}
             ]
        '''
        var_item_list = []
        for item in body:
            var_tmp = {item["inputKey"]: item["inputValue"]}
            var_item_list.append(var_tmp)
        return var_item_list

    if body_type == 'hooks':
        hook_item_list = []  # 字符串列表
        for item in body:
            hook_item = "${{{func}({var})}}".format(func=item["selectHooks"], var=item["inputValue"])
            hook_item_list.append(hook_item)
        return hook_item_list


def vue_case_pre_cases_to_db(pre_cases_body: List[Dict]) -> List[List]:
    form_pre_cases = []
    # form_pre_cases = eval(pre_cases_body)
    for item in pre_cases_body:
        pre_case_id, pre_case_name = item.get('id'), item.get('name')
        pre_case_list = [str(pre_case_id), pre_case_name]
        form_pre_cases.append(pre_case_list)
    return form_pre_cases


def vue_request_to_db(request_body) -> Dict:
    #用于auth和mock请求体和请求头的数据转换
    form_request = request_body
    logger.info(form_request)

    data_type = form_request.get("request").pop("type")
    data_content = form_request.get("request").pop("content")
    if data_type == "json":
        form_request.get('request').setdefault(data_type, json.loads(data_content.replace('\n', '')))
    elif data_type == 'data' or data_type == 'params':
        form_request.get('request').setdefault(data_type, vue_to_db_dict('variables', data_content))
    # When edit case, headers maybe is None
    headers = form_request.get("request").get("headers", '')
    if headers:
        headers = form_request.get("request").pop("headers")
        form_request.get('request').setdefault('headers', vue_to_db_dict('headers', headers))
    if headers == []:
        form_request.get("request").pop("headers")

    return form_request


def vue_case_request_to_db(request_body) -> Dict:
    # form_request = eval(request_body)
    form_request = request_body
    logger.info(form_request)

    data_type = form_request.get("test").get("request").pop("type")
    data_content = form_request.get("test").get("request").pop("content")
    if data_type == "json":
        form_request.get('test').get('request').setdefault(data_type, json.loads(data_content.replace('\n', '')))
    elif data_type == 'data' or data_type == 'params':
        form_request.get('test').get('request').setdefault(data_type, vue_to_db_dict('variables', data_content))
    # When edit case, headers maybe is None
    headers = form_request.get("test").get("request").get("headers", '')
    if headers:
        headers = form_request.get("test").get("request").pop("headers")
        form_request.get('test').get('request').setdefault('headers', vue_to_db_dict('headers', headers))
    if headers == []:
        form_request.get("test").get("request").pop("headers")

    form_request['test']['extract'] = vue_to_db_list('extract', form_request.get('test', {}).get('extract', []))
    form_request['test']['validate'] = vue_to_db_list('validate', form_request.get('test', {}).get('validate', []))
    form_request['test']['variables'] = vue_to_db_list('variables', form_request.get('test', {}).get('variables', []))
    form_request['test']['parameters'] = vue_to_db_list('parameters',
                                                        form_request.get('test', {}).get('parameters', []))
    form_request['test']['setup_hooks'] = vue_to_db_list('hooks', form_request.get('test', {}).get('setup_hooks', []))
    form_request['test']['teardown_hooks'] = vue_to_db_list('hooks',
                                                            form_request.get('test', {}).get('teardown_hooks', []))

    return form_request


def vue_suite_to_db(cases_body: List[Dict]) -> List[List]:
    """
    将vue中的套件用例列表格式，转换成数据库中格式
    list[dict]
    [{id: '100', name: 'PC登录认证', status: True}, {id: '101', name: 'PC登录认证', status: False},...]
    list[list]
    [['100', 'PC登录认证', '启用中'], ['101', 'PC登录认证', '暂停中'],...]
    """
    form_cases = []
    disabled_count = 0
    # cases_body = eval(cases_body)

    for case in cases_body:
        case_id, case_name, case_status = str(case['id']), case['name'], case['status']
        if case_status:
            case_tmp = [case_id, case_name, '启用中']
        else:
            case_tmp = [case_id, case_name, '暂停中']
            disabled_count += 1
        form_cases.append(case_tmp)

    return form_cases, disabled_count


def db_value_to_vue_type_value(var_value: Any) -> tuple:
    """
    根据变量的值，提取成vue变量对应的类型
    :param var_value: variables value in database
    :return: tuple: (vue variable type, vue value)
    """
    try:
        if isinstance(var_value, str):
            var_type = 'string'
        elif isinstance(var_value, bool):
            var_type = "boolean"
            var_value = str(var_value)
        elif isinstance(var_value, int):
            var_type = "int"
        elif isinstance(var_value, float):
            var_type = 'float'
            var_value = str(var_value)
        elif isinstance(var_value, list):
            var_type = "file"
            var_value = var_value[0]
        else:
            var_type = 'exception'

    except ValueError:
        logger.error('变量值：{var_value},提取变量类型{var_type}失败'.format(var_type=var_type, var_value=var_value))
        return 'exception'

    return var_type, var_value


function_regex_compile = re.compile(r"\$\{(\w+)\(([\$\w\.\-/\s=,]*)\)\}")
funcs_list = ["executeSql", "execute_sql", "execute_bat", "execute_es"]
funcs_map = dict(执行sql语句MySQL="executeSql", 执行sql语句ConfigDataBase="execute_sql",
                 执行bat脚本="execute_bat", 执行es语句="execute_es")


def _get_keywords_for_func(func_name, func_map):
    """
    将function name转换为对应的前端显示名称
    :param func_name: function name
    :param func_map:
    :return: str: function显示名称
    """
    for key, value in func_map.items():
        if value == func_name:
            return key
    else:
        return ""


def db_dict_to_vue_list(body_type: str, body: Dict) -> List:
    """
    根据变量的值，提取成vue变量对应的类型
    :param body_type: string :结构类型
    :param body: dict: 结构体-字典
    :return: list: 转换后，前端显示的结构体-列表
    """

    if body_type == 'data' or body_type == 'params':
        index: int = 1
        var_item_list = []
        if not body:
            return var_item_list
        for key, value in body.items():
            var_type, var_value = db_value_to_vue_type_value(value)
            var_tmp = {
                'id': str(index),
                'inputKey': key,
                'selectType': var_type,
                'inputValue': var_value
            }
            var_item_list.append(var_tmp)
            index += 1

        return var_item_list

    if body_type == 'headers':
        '''
            'headers': {'token': '$token', 'token': '$token', ...}
            
            headers: [
                {
                  id: '1',
                  inputKey: 'headers123',
                  inputValue: '134'
                },
                {
                  id: '2',
                  inputKey: 'headers223',
                  inputValue: '234'
                }
              ],   
        '''
        index: int = 1
        header_item_list = []
        if not body:
            return var_item_list
        for key, value in body.items():
            header_key, header_value = key, value
            header_tmp = {
                'id': str(index),
                'inputKey': header_key,
                'inputValue': header_value
            }
            header_item_list.append(header_tmp)
            index += 1

        return header_item_list


def db_to_vue_list(body_type: str, body: List) -> List:
    """
    根据变量的值，提取成vue变量对应的类型
    :param body_type: string :结构类型
    :param body: list: 结构体-列表
    :return: list: 转换后，前端显示的结构体-列表
    """
    # config
    if body_type == 'database':
        db_item_list = []
        for item in body:
            for key, value in item.items():
                var_type, var_value = db_value_to_vue_type_value(value)
                db_tmp = {
                    'key': key,
                    'type': var_type,
                    'value': var_value
                }
            db_item_list.append(db_tmp)
        return db_item_list

    if body_type == 'extract':
        index: int = 1
        extract_item_list = []
        if not body:
            return extract_item_list
        for item in body:
            for extract_key, extract_value in item.items():
                extract_tmp = {
                    'id': str(index),
                    'inputKey': extract_key,
                    'inputValue': extract_value
                }
            extract_item_list.append(extract_tmp)
            index += 1
        return extract_item_list

    if body_type == 'validate':
        index: int = 1
        val_item_list = []
        if not body:
            return val_item_list
        for item in body:
            for key, value in item.items():
                comparator_type, content_list = key, value
                type, expected = db_value_to_vue_type_value(content_list[1])
                var_tmp = {
                    'id': str(index),
                    'inputCheck': content_list[0],
                    'selectComparator': comparator_type,
                    'selectType': type,
                    'inputExpected': expected
                }
            val_item_list.append(var_tmp)
            index += 1
        return val_item_list

    if body_type == 'variables':
        index: int = 1
        var_item_list = []
        if not body:
            return var_item_list
        for item in body:
            for key, value in item.items():
                var_type, var_value = db_value_to_vue_type_value(value)
                var_tmp = {
                    'id': str(index),
                    'inputKey': key,
                    'selectType': var_type,
                    'inputValue': var_value
                }
            var_item_list.append(var_tmp)
            index += 1
        return var_item_list

    if body_type == 'parameters':
        index: int = 1
        parameters_list = []
        if not body:
            return parameters_list
        for item in body:
            for parameter_key, parameter_value in item.items():
                parameter_tmp = {
                    'id': str(index),
                    'inputKey': parameter_key,
                    'inputValue': parameter_value
                }
            parameters_list.append(parameter_tmp)
            index += 1
        return parameters_list

    if body_type == 'hooks':
        index: int = 1
        hooks_list = []
        if not body:
            return hooks_list
        for hooks in body:
            hook_tmp = {}
            func_match = function_regex_compile.match(hooks)
            if func_match:
                func_name = func_match.group(1)
                paras = func_match.group(2)
                # hook_tmp.setdefault("value", _get_keywords_for_func(func_name, funcs_map))
                # hook_tmp.setdefault("key", paras)
                hook_tmp = {
                    'id': str(index),
                    'selectHooks': func_name,
                    'inputValue': paras
                }
            hooks_list.append(hook_tmp)
        return hooks_list


def db_config_body_to_vue(config_body):
    form_config_body = eval(config_body)
    form_config_body['database'] = db_to_vue_list('database', form_config_body.get('database'))
    form_config_body['variables'] = db_to_vue_list('variables', form_config_body.get('variables'))
    return form_config_body


def db_case_pre_cases_to_vue(pre_cases_body: List[List]) -> List[Dict]:
    form_pre_cases = []
    pre_cases_body = eval(pre_cases_body)
    for item in pre_cases_body:
        pre_case_id, pre_case_name = item[0], item[1]
        pre_case_dict = {
            'id': int(pre_case_id),
            'name': pre_case_name
        }
        form_pre_cases.append(pre_case_dict)
    return form_pre_cases


def db_case_request_to_vue(request_body) -> Dict:
    form_request = eval(request_body)

    request_key_list = list(form_request.get('test').get('request').keys()).copy()

    if 'data' in request_key_list:
        print('111')
        form_request.get('test').get('request').setdefault('type', 'data')
        data = form_request.get('test').get('request').pop('data')
        form_request.get('test').get('request').setdefault('content', db_dict_to_vue_list('data', data))
    elif 'params' in request_key_list:
        form_request.get('test').get('request').setdefault('type', 'params')
        params = form_request.get('test').get('request').pop('params')
        form_request.get('test').get('request').setdefault('content', db_dict_to_vue_list('data', params))
    elif 'json' in request_key_list:
        form_request.get('test').get('request').setdefault('type', 'json')
        jsonStr = form_request.get('test').get('request').pop('json')
        jsonStr = json.dumps(jsonStr, ensure_ascii=False)
        form_request.get('test').get('request').setdefault('content', jsonStr)
    else:
        form_request.get('test').get('request').setdefault('type', 'data')
        form_request.get('test').get('request').setdefault('content', [])

    if form_request.get('test').get('request').get('headers'):
        form_request['test']['request']['headers'] = db_dict_to_vue_list('headers',
                                                                         form_request.get('test').get('request').get(
                                                                             'headers'))

    form_request['test']['extract'] = db_to_vue_list('extract', form_request.get('test', {}).get('extract', []))
    form_request['test']['validate'] = db_to_vue_list('validate', form_request.get('test', {}).get('validate', []))
    form_request['test']['variables'] = db_to_vue_list('variables', form_request.get('test', {}).get('variables', []))
    form_request['test']['parameters'] = db_to_vue_list('parameters',
                                                        form_request.get('test', {}).get('parameters', []))
    form_request['test']['setup_hooks'] = db_to_vue_list('hooks', form_request.get('test', {}).get('setup_hooks', []))
    form_request['test']['teardown_hooks'] = db_to_vue_list('hooks',
                                                            form_request.get('test', {}).get('teardown_hooks', []))

    return form_request


def db_request_to_vue(request_body) -> Dict:
    #用于auth和mock请求体和请求头的数据转换

    form_request = eval(request_body)
    request_key_list = list(form_request.get('request').keys()).copy()

    if 'data' in request_key_list:
        form_request.get('request').setdefault('type', 'data')
        data = form_request.get('request').pop('data')
        form_request.get('request').setdefault('content', db_dict_to_vue_list('data', data))
    elif 'params' in request_key_list:
        form_request.get('request').setdefault('type', 'params')
        params = form_request.get('request').pop('params')
        form_request.get('request').setdefault('content', db_dict_to_vue_list('data', params))
    elif 'json' in request_key_list:
        form_request.get('request').setdefault('type', 'json')
        jsonStr = form_request.get('request').pop('json')
        jsonStr = json.dumps(jsonStr, ensure_ascii=False)
        form_request.get('request').setdefault('content', jsonStr)
    else:
        form_request.get('request').setdefault('type', 'data')
        form_request.get('request').setdefault('content', [])

    if form_request.get('request').get('headers'):
        form_request['request']['headers'] = db_dict_to_vue_list('headers',form_request.get('request').get('headers'))


    return form_request


def db_suite_to_vue(cases_body: List[List]) -> List[Dict]:
    """
    将数据库中格式，转换成vue中的套件用例列表格式

    list[list]
    [['100', 'PC登录认证', '启用中'], ['101', 'PC登录认证', '暂停中'],...]

    list[dict]
    [{id: 100, name: 'PC登录认证', status: True}, {id: 101, name: 'PC登录认证', status: False},...]

    """
    form_cases = []
    cases_body = eval(cases_body)

    for case in cases_body:
        case_id, case_name, case_status = case[0], case[1], case[2]
        case_tmp = {
            'id': int(case_id),
            'name': case_name,
            'status': True if (case_status == '启用中') else False
        }

        form_cases.append(case_tmp)

    return form_cases
